package fina2.dcs.mail.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.log4j.Logger;
import org.jboss.annotation.ejb.Management;
import org.jboss.annotation.ejb.Service;

import fina2.dcs.common.resources.MessageBundleReader;
import fina2.dcs.common.upload.UploadedFileInfo;
import fina2.dcs.mail.MailReaderBase;
import fina2.dcs.mail.dbo.bean.MailReplyLocal;
import fina2.dcs.mail.dbo.domain.MailReply;
import fina2.dcs.mail.manager.AttachInfo;
import fina2.dcs.mail.manager.MailPropsKey;
import fina2.dcs.mail.manager.MailRobot;
import fina2.dcs.mail.manager.MailRobotImpl;
import fina2.dcs.mail.manager.MailUser;
import fina2.dcs.security.User;
import fina2.dcs.security.UserLocal;
import fina2.dcs.system.SysProperty;
import fina2.dcs.system.SysPropertyLocal;
import fina2.dcs.upload.UploadFileStatus;
import fina2.dcs.upload.UploadedFileLocal;

@Service(objectName = "dcs:service=mailService")
@Management(value = MailService.class)
public class MailServiceImpl implements MailService {
	private Timer timer = new Timer(true);
	private TimerTask timerTask;
	private Logger log = Logger.getLogger(getClass());

	private MailRobot mailRobot;
	private List<MailReaderBase> mailReadersSessions;

	private SysPropertyLocal sysPropertyLocal;

	private Date mailLastReadDate = null;

	private static final String RESPONCE_MESSAGE_SUBJECT = "DCS Email Robot";

	private long interval = 1l;

	private Long mailLimitTimeout = null;

	@Override
	public void start() {
		log.info("Staritng Mail Srvice!");
		timerTask = new TimerTask() {
			public void run() {
				try {
					synchronizeMail();
				} catch (Throwable t) {
					log.error("Error during synchronizing email!\n");
					log.error(t.getMessage(), t);
				}
			}
		};

		timer.schedule(timerTask, 60000, interval);
	}

	private long loadInterval() {
		try {
			InitialContext initialContext = new InitialContext();
			sysPropertyLocal = (SysPropertyLocal) initialContext.lookup("SysPropertySession/local");
			String maxCheckInterval = sysPropertyLocal.findPropValue(MailPropsKey.MAIL_CHECK_INTERVAL);
			if ((maxCheckInterval != null) && (!maxCheckInterval.trim().equals("")))
				interval = Long.parseLong(maxCheckInterval);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
		return interval;
	}

	private void loadParametersAndCreaeteSessions() {
		Map<String, String> allPropertiesMap = new HashMap<String, String>();
		try {
			InitialContext initialContext = new InitialContext();
			sysPropertyLocal = (SysPropertyLocal) initialContext.lookup("SysPropertySession/local");

			allPropertiesMap = sysPropertyLocal.loadAllProperties();

			String mailLastReadDateString = allPropertiesMap.get(MailPropsKey.LAST_READ_DATE);
			if (isPropertyNull(mailLastReadDateString)) {
				return;
			}

			if (!mailLastReadDateString.trim().equals("")) {
				mailLastReadDate = new Date(Long.parseLong(mailLastReadDateString));
			} else {
				mailLastReadDate = null;
			}

			// Load Mail Limit Timeout.
			String mailLimitTimeoutString = allPropertiesMap.get(MailPropsKey.MAIL_LIMIT_TIMEOUT);
			if (isPropertyNull(mailLimitTimeoutString)) {
				mailLimitTimeout = null;
			} else {
				if (mailLimitTimeoutString.equals("")) {
					mailLimitTimeout = null;
				} else {
					try {
						mailLimitTimeout = Long.parseLong(mailLimitTimeoutString);
					} catch (Exception ex) {
						log.info(ex.getMessage(), ex);
						mailLimitTimeout = null;
					}
				}
			}

			log.info("Last Mail read date - " + mailLastReadDate.toString());

			String mailConnectionTypeString = allPropertiesMap.get(MailPropsKey.MAIL_CONNECTION_TYPE);
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Got Connection Type");
			if (isPropertyNull(mailConnectionTypeString)) {
				return;
			}

			int connectionType = Integer.parseInt(mailConnectionTypeString);
			log.info("Connection Type : " + connectionType);

			String mailUser = allPropertiesMap.get(MailPropsKey.MAIL_USER);
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Got Mail User");
			if (isPropertyNull(mailUser)) {
				return;
			}

			String mailAddress = allPropertiesMap.get(MailPropsKey.MAIL_ADDRESS);
			String password = allPropertiesMap.get(MailPropsKey.MAIL_PASSWORD);
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Got Mail PWD");
			if (isPropertyNull(password)) {
				return;
			}
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Creating Mail User Object");
			MailUser firstUser = new MailUser(mailUser, password, connectionType, allPropertiesMap);
			firstUser.setMailAddress(mailAddress);
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Mail User Object Created");
			List<MailUser> list = new ArrayList<MailUser>();
			list.add(firstUser);
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Creating Mail Robot IMPL");
			mailRobot = new MailRobotImpl(list);
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; MailRobotImpl Created");
			mailReadersSessions = mailRobot.createMailReadSessions();
			log.info("Thread : " + Thread.currentThread().getId() + " ; Name=" + Thread.currentThread().getName() + "; Created Mail Readers Session");
		} catch (NamingException e) {
			log.error(e.getMessage(), e);
		}
	}

	private boolean isPropertyNull(String property) {
		if (property == null) {
			log.warn("\t" + MailPropsKey.MAIL_CONNECTION_TYPE + " - Propery not found.");
			return true;
		}
		return false;
	}

	@Override
	public void synchronizeMail() throws NamingException {
		try {
			loadInterval();
			Thread.sleep(interval);
		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}

		log.info("Start Synchronizing mail - - - - - - -");

		loadParametersAndCreaeteSessions();

		// Sent "Not Send" messages
		for (MailReaderBase m : mailReadersSessions) {
			sentNotSendMessages(m.getMailUser());
		}

		Map<MailUser, List<AttachInfo>> mailUsersAttaches = mailRobot.readMails(mailReadersSessions, mailLastReadDate);

		if (mailUsersAttaches.size() > 0) {
			Map<String, List<UploadedFileInfo>> nameStatMap = new HashMap<String, List<UploadedFileInfo>>();
			Map<String, byte[]> nameContentMap = new HashMap<String, byte[]>();

			for (Entry<MailUser, List<AttachInfo>> e : mailUsersAttaches.entrySet()) {

				TreeSet<Date> mailSendDates = new TreeSet<Date>();

				MailUser mailUser = e.getKey();

				List<AttachInfo> mailUserFiles = e.getValue();

				if (mailUserFiles != null) {
					for (AttachInfo ai : mailUserFiles) {
						nameContentMap = ai.getAttachFiles();
						mailSendDates.add(ai.getReceivedDate());

						InitialContext initialContext = new InitialContext();
						UserLocal userLocal = (UserLocal) initialContext.lookup("UserSession/local");

						// Ignore Mail
						boolean ignore = false;
						String notReadMails = sysPropertyLocal.findPropValue(MailPropsKey.MAIL_NOT_READ);
						if (notReadMails != null && (!notReadMails.equals(""))) {
							StringTokenizer tokenizer = new StringTokenizer(notReadMails, ",");
							while (tokenizer.hasMoreElements()) {
								if (tokenizer.nextElement().equals(ai.getAddress())) {
									ignore = true;
									log.info(ai.getAddress() + " - is in mail ignore list.");
								}
							}
						}

						if (!ignore) {

							User user = new User();

							User findUser = userLocal.findByEmail(ai.getAddress());

							if (findUser == null) {
								log.info(ai.getAddress() + " - is unknown  User.");

								// Set Default User.
								user.setLogin(ai.getAddress());
								user.setId(userLocal.getUserId("sa"));

								String responceUnknow = sysPropertyLocal.findPropValue(MailPropsKey.MAIL_RESPONCE_UNKNOWN_USER);
								if (responceUnknow == null) {
									return;
								} else {
									int resp = Integer.parseInt(responceUnknow = responceUnknow.trim());
									if (resp < 0) {
										return;
									}
								}
							} else {
								user.setLogin(findUser.getLogin());
								user.setId(findUser.getId());
								log.info(ai.getAddress() + " is " + user.getLogin() + "'s Email.");
							}

							log.info("Starting data process from email");

							try {
								UploadedFileLocal uploadFileLocal = (UploadedFileLocal) initialContext.lookup("UploadedFileSession/local");
								nameStatMap = uploadFileLocal.upload(nameContentMap, user);
							} catch (Exception ex) {
								log.error(ex.getMessage(), ex);
							} finally {
								log.info("EnD Date Process");
							}

							if (!mailUser.getMailAddress().equals(ai.getAddress())) {
								if (sysPropertyLocal.findPropValue(MailPropsKey.MAIL_SEND_RESPONCE_ENABLE).equals("true")) {
									String ccMails = sysPropertyLocal.findPropValue(MailPropsKey.MAIL_RESPONCE_CC);

									String[] cc = null;
									if (!ccMails.trim().equals("")) {
										cc = ccMails.split(",");
									}

									String responceText = getResponceText(nameStatMap);

									try {
										if (!mailRobot.isConnectedSender()) {
											mailRobot.connectSender(mailUser);
										}

										// BCC parameter is NULL
										mailRobot.sentResponce(responceText, RESPONCE_MESSAGE_SUBJECT, cc, null, ai.getAddress());
										log.info("Send Mail TO : " + ai.getAddress() + " | CC : " + Arrays.toString(cc));
									} catch (Throwable ex) {
										MailReplyLocal replyLocale = (MailReplyLocal) initialContext.lookup("MailReplyBean/local");
										MailReply mailReply = new MailReply();
										mailReply.setFrom(mailUser.getUser());
										mailReply.setTo(ai.getAddress());
										mailReply.setCc(cc);
										mailReply.setSender(ai.getAddress());
										mailReply.setContent(responceText);
										mailReply.setDate(ai.getReceivedDate());
										mailReply.setSendStatus(MailPropsKey.Mail_REPLAY_NOT_SEND);
										replyLocale.saveMailReply(mailReply);

										log.info("Save MailReplay - " + mailReply);

										log.error(ex.getMessage(), ex);
									}

								} else {
									log.info("Message Sending is Disable!");
								}
							} else {
								log.info("To and Sender mails are equal.(to : " + ai.getAddress() + ", Sender : " + mailUser.getUser() + ")");
							}

						}
					}
					mailRobot.closeSenderConnection();
				}

				// Save Last Read Mail send date.
				if (mailSendDates.size() > 0) {
					Date date = null;
					try {
						date = mailSendDates.last();
					} catch (Exception ex) {
						log.error(ex.getMessage(), ex);
					}
					if (date != null) {
						SysProperty property = new SysProperty();
						property.setPropKey(MailPropsKey.LAST_READ_DATE);
						property.setValue(Long.toString(date.getTime()));
						sysPropertyLocal.updateProperty(property);
						log.info("Save Last Read date - " + date.toString());
						mailLastReadDate = date;
					}
				}
			}
		}

		log.info("- - - - - - - End Synchronizing mail.");
	}

	@Override
	public void stop() {
		log.info("Stop Mail Service!");
	}

	@Override
	public void destroy() {
		log.info("Destory Mail Service!");
	}

	// Sent 'Not Send' status Mails.
	private void sentNotSendMessages(MailUser mailUser) {
		try {
			InitialContext initialContext = new InitialContext();
			MailReplyLocal replyLocale = (MailReplyLocal) initialContext.lookup("MailReplyBean/local");

			List<MailReply> mailReplies = replyLocale.loadMails(MailPropsKey.Mail_REPLAY_NOT_SEND);
			log.info(" 'Not Send' Mails  are : " + mailReplies);

			for (MailReply mailReply : mailReplies) {
				try {
					if (mailLimitTimeout != null && mailLimitTimeout > 0L) {
						try {
							long sleepTime = mailLimitTimeout + (long) (Math.random() * 1000);

							log.info("Mail Limit Timeout is " + mailLimitTimeout);
							log.info("Sleep Mail sender Service " + sleepTime + " milis");

							Thread.sleep(sleepTime);
						} catch (Exception ex) {
							log.error(ex.getMessage(), ex);
						}
					}

					if (!mailRobot.isConnectedSender()) {
						mailRobot.connectSender(mailUser);
					}

					mailRobot.sentResponce(mailReply.getContent(), RESPONCE_MESSAGE_SUBJECT, mailReply.getCc(), mailReply.getBcc(), mailReply.getTo());

					log.info("Send 'Not send' status Mails -  " + mailReply);

					replyLocale.updateMailStatus(mailReply.getId(), MailPropsKey.Mail_REPLAY_SEND);
				} catch (Exception ex) {
					log.error(ex.getMessage(), ex);
				}
			}

			mailRobot.closeSenderConnection();

		} catch (Exception ex) {
			log.error(ex.getMessage(), ex);
		}
	}

	private String getResponceText(Map<String, List<UploadedFileInfo>> map) {

		StringBuilder sb = new StringBuilder();

		String propVal = sysPropertyLocal.findPropValue(MailPropsKey.RESPONSE_LANGUAGE);
		ResourceBundle messageBundle;
		if (propVal == null || propVal.trim().equals("")) {
			log.info("Could not find default language in system, setting to english");
			messageBundle = MessageBundleReader.loadMessageBundle();
		} else {
			try {
				if (propVal.toLowerCase().trim().equals("en_us")) {
					log.info("Setting response language to English");
					messageBundle = MessageBundleReader.loadMessageBundle();
				} else {
					log.info("Setting reponse language to " + propVal);
					messageBundle = MessageBundleReader.loadMessageBundle(propVal.substring(0, 2));
				}
			} catch (Exception ex) {
				log.error(ex.getMessage(), ex);
				messageBundle = MessageBundleReader.loadMessageBundle();
			}
		}

		sb.append(messageBundle.getString("fina2.dcs.mail.responce.header"));

		sb.append("<UL>");
		if (map != null) {

			for (Entry<String, List<UploadedFileInfo>> entry : map.entrySet()) {
				String fileName = entry.getKey();
				List<UploadedFileInfo> fileInfos = entry.getValue();

				sb.append("<LI>");
				sb.append(fileName);
				sb.append(" | Status: ");

				for (UploadedFileInfo uploadedFileInfo : fileInfos) {
					sb.append(getStatusesString(uploadedFileInfo.getStatus(), messageBundle));
				}

				sb.append("</LI>");
			}
		}

		sb.append("</UL>");

		int responceCace = getCase(map);

		switch (responceCace) {
		case 0: {
			sb = new StringBuilder();
			sb.append(messageBundle.getString("fina2.dcs.mail.responce.empty.header"));
			break;
		}
		case 2: {
			sb.append(messageBundle.getString("fina2.dcs.mail.responce.errorMail"));

			for (Entry<String, List<UploadedFileInfo>> entry : map.entrySet()) {

				String fileName = entry.getKey();

				List<UploadedFileInfo> fileInfos = entry.getValue();

				for (UploadedFileInfo info : fileInfos) {
					int statusId = info.getStatus();

					if (!(statusId == UploadFileStatus.UPLOADED_CONVERTED.ordinal())) {
						sb.append("<p>");
						sb.append(fileName);
						sb.append("</p>");

						List<String> list = info.getMessageCodes();
						if (list != null) {
							sb.append("<UL>");
							for (String s : list) {
								sb.append("<LI>");
								String text = messageBundle.getString(s);
								if (text == null) {
									text = s;
								}

								String additionalInfo = info.getAdditionalInfo();
								if (additionalInfo != null) {
									// Add Additional Info
									text += " " + additionalInfo;
								}

								sb.append(text);
								sb.append("</LI>");
							}
							sb.append("</UL>");
						}

					}
				}
			}
			break;
		}
		}

		sb.append(messageBundle.getString("fina2.dcs.mail.responce.footer"));

		return sb.toString();

	}

	private int getCase(Map<String, List<UploadedFileInfo>> map) {
		if (map.size() == 0) {
			return 0;
		}
		for (Entry<String, List<UploadedFileInfo>> entry : map.entrySet()) {
			List<UploadedFileInfo> fileInfos = entry.getValue();

			for (UploadedFileInfo info : fileInfos) {
				if (!(info.getStatus() == UploadFileStatus.UPLOADED_CONVERTED.ordinal())) {
					return 2;
				}
			}
		}
		return 1;
	}

	private String getStatusesString(Integer statusId, ResourceBundle bundle) {
		String status = null;

		if (statusId == UploadFileStatus.CONVERTED.ordinal()) {
			status = bundle.getString(UploadFileStatus.CONVERTED.getCode());
		} else if (statusId == UploadFileStatus.REJECTED.ordinal()) {
			status = bundle.getString(UploadFileStatus.REJECTED.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED_CONVERTED.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED_CONVERTED.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED_ERROR_PROCESSING.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED_ERROR_PROCESSING.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED_NOT_CONVERTED.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED_NOT_CONVERTED.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED_NOT_VALIDATED.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED_NOT_VALIDATED.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED_PROCESSED.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED_PROCESSED.getCode());
		} else if (statusId == UploadFileStatus.UPLOADED_VALIDATED.ordinal()) {
			status = bundle.getString(UploadFileStatus.UPLOADED_VALIDATED.getCode());
		}
		return status;
	}
}
